
#define GL_GLEXT_PROTOTYPES

#include "openglframe.h"
#include <algorithm>

OpenGLFrame::OpenGLFrame (QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags f)
   : QGLWidget (parent, shareWidget, f), 
     arcball(1,1) {
   
   // for keyboard use
   setFocusPolicy(Qt::StrongFocus);
   
   cursor3D = Point3(0,0,0);
   interactionMode = SELECTION_MODE;
   
   skeleton = new USkeleton;
   model = 0;
   texFloor = 0;
   idLastJoint = -1;
   idSelected = -1;
   
   paintCursorW = 8;
   paintCursorH = 8;
   
   fovy = 40.0;
   zoomFactor = 1.0;
   meshrendermode_mask = 0x20;
   
   isProjOrtho = true; // orthographic projection
   is_lightingOn = true;
   clusteringOnBones = true;
   viewSkeleton = true;
   idVertex = -1;
   
   viewitems_mask = 0x0; 
   viewitems_mask = viewitems_mask | MODELDEF;
   viewitems_mask = viewitems_mask | SKELETONDEF;
   viewitems_mask = viewitems_mask | KEYPOINTS;
   viewitems_mask = viewitems_mask | CONSTRAINTS;
   viewitems_mask = viewitems_mask | FLOOR;
   viewitems_mask = viewitems_mask | INFO;
   rigskin_mask = 0x08;
   
   // Floor and shadow specificacion
   floor[0] = Point3(-4.5,-1.0, 4.5);
   floor[1] = Point3( 4.5,-1.0, 4.5);
   floor[2] = Point3( 4.5,-1.0,-4.5);
   floor[3] = Point3(-4.5,-1.0,-4.5);
   pfloor = Plane3(floor[0], floor[2], floor[1]);
   
   float l[] = {3.0, 5.0, 5.0}; // Coordinates of the light source
   float e[] = {0.0, -0.99, 0.0};  // Point of the floor plane
   float n[] = {0.0, -1.0, 0.0}; // Normal vector for the plane
   createShadowProjectionMatrix(l,e,n);
   
   generateColors();
}

OpenGLFrame::~OpenGLFrame() {
   
   if (model!=0)        delete model;
   if (skeleton!=0)     delete skeleton;
}

/// Sets the interaction mode
void OpenGLFrame::setInteractionMode (InteractionMode m) {
   
   interactionMode = m;
}

bool OpenGLFrame::loadShape(const QString& filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   Mesh *newmodel = 0;
   if (extension == "off")
      newmodel = Mesh::load_off(filename.toStdString().c_str());
   //~ else if (extension == "ply")
      //~ newmodel = Mesh::load_ply(filename.toStdString().c_str());
   else 
      return false;
   
   if (newmodel) {
      if (model != 0) delete model;
      model = newmodel;
      
      /// OpenGL vertex arrays setup
      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_NORMAL_ARRAY);
      typedef Mesh::Vertex Vertex;
      Vertex_iterator vi = model->vertices_begin();
      glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &(vi->point()[0]));
      glNormalPointer(GL_FLOAT, sizeof(Vertex), &(vi->normal()[0]));
      
      genWorldLimits();
      setGLview();
      sceneT.identity();
      
      return true;
   }
   return false;
}

void OpenGLFrame::saveSkeleton(const QString& filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   
   if (extension == "ske") {
      assert(skeleton != 0);
      skeleton->export_ske(filename.toStdString().c_str());
   }
   else if (extension == "skel") {
      //assert(skeleton != 0);
      //skeleton->export_skel(filename.toStdString().c_str());
   }
}

bool OpenGLFrame::loadSkeleton(const QString& filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   
    if (extension == "ske") {
      if (skeleton != 0) delete skeleton;
      skeleton = USkeleton::load(filename.toStdString().c_str());
      cout<<"joints: "<<skeleton->nJoints()<<" nBones: "<<skeleton->nBones()<<endl;
      return true;
   }
   else
      return false;
}

/// OpenGL initialization.
void OpenGLFrame::initializeGL() {
   
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_LIGHT1);
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   
   // lighting setup
   GLfloat left_light_position[] =  {0.0f, 2.0f, 2.0f, 0.0f};
   GLfloat right_light_position[] = {0.0f, -2.0f, 2.0f, 0.0f};
   GLfloat left_diffuse_light[] =  {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat right_diffuse_light[] = {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat color_front[] = {0.0f, 0.5f, 1.0f, 1.0f};
   GLfloat colr_back[] = {1.0f, 0.0f, 0.0f, 1.0f};
   
   glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
   glLightfv(GL_LIGHT1, GL_POSITION, right_light_position);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, left_diffuse_light);
   glLightfv(GL_LIGHT1, GL_DIFFUSE, right_diffuse_light);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color_front);
   glMaterialfv(GL_BACK, GL_DIFFUSE, colr_back);
   
   glClearColor(1,1,1,1);
   glColor4f(1,1,0,1);
   meshrendermode_mask = SMOOTH;
   sceneT.identity();
   
   std::time(&start);
   frame = 0;
   
   if (idSelected<0)  setCursor(Qt::ArrowCursor);
   else               setCursor(Qt::PointingHandCursor);
   
   genWorldLimits();
   
   texFloor = new Texture("icons/floor4.png", GL_TEXTURE_2D);
   //~ setGLview();
}

/// A simple opengl drawing callback
void OpenGLFrame::paintGL() {
   
   //~ setGLview();
   
   //~ glClearStencil(0x1);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(cameye[0],cameye[1],cameye[2],camcenter[0],camcenter[1],camcenter[2],0,1,0);
   
   if (model == 0) return;
   
   glMultMatrixf(&sceneT);
   
   // Draw the model
   if ((viewitems_mask & MODELDEF) == MODELDEF) {
      glDrawModel(model, meshrendermode_mask);
      glDrawRiggingSkinning(model, skeleton, rigskin_mask, clusteringOnBones);
   }
   
   if (viewSkeleton)
      glDrawSkeleton(skeleton);
   
   if (interactionMode == PAINTING_MODE) setCursor(Qt::PointingHandCursor);
   else                                  setCursor(Qt::ArrowCursor);
   
   if (rigskin_mask & SKINNING)
      glDrawVertexSkinning(model, skeleton, idVertex, clusteringOnBones);
   
   //~ glBegin(GL_POINTS);
   //~ glEnd();
   
   // FPS info
   //~ if ((viewitems_mask & INFO) == INFO)
      //~ renderInfo();
}

void OpenGLFrame::renderInfo() {
   
   // Render info
   if ((viewitems_mask & INFO) == INFO and model != 0) {
      glDisable(GL_LIGHTING);
      glDisable(GL_DEPTH_TEST);
      
      frame++;
      std::time(&end);
      double ddifftime = std::difftime(end, start);
      if (ddifftime > 1) {
         cfps = frame/ddifftime;
         start = end;
         frame = 0;
      }
      
      QString strInfoNv=QString("vertices: %1").arg(model->nvertices());
      QString strInfoNf=QString("faces: %1").arg(model->nfaces());
      QString strInfoFPS=QString("FPS: %1").arg(cfps,7,'f',1);
      
      glColor4f(0.0, 0.0, 0.0, 1.0);
      renderText(30,15, strInfoNv);
      renderText(30,30, strInfoNf);
      renderText(30,75, strInfoFPS);
      
      glEnable(GL_LIGHTING);
      glEnable(GL_DEPTH_TEST);
   }
}

void OpenGLFrame::drawFloor() {
   
   glEnable(GL_TEXTURE_2D);
   texFloor->bind();
   
   glDisable(GL_LIGHTING);
   glPolygonMode(GL_FRONT, GL_FILL);
   glPolygonMode(GL_BACK, GL_LINE);
   glColor3f(1.0,1.0,1.0);
   glBegin(GL_QUADS);
   glNormal3f(0.0,1.0,0.0);
   glTexCoord2d(0.0,0.0); glVertex3f(floor[0][0],floor[0][1], floor[0][2]);
   glTexCoord2d(1.0,0.0); glVertex3f(floor[1][0],floor[1][1], floor[1][2]);
   glTexCoord2d(1.0,1.0); glVertex3f(floor[2][0],floor[2][1], floor[2][2]);
   glTexCoord2d(0.0,1.0); glVertex3f(floor[3][0],floor[3][1], floor[3][2]);
   glEnd();
   glEnable(GL_LIGHTING);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   
   glDisable(GL_TEXTURE_2D);
}

void OpenGLFrame::createShadowProjectionMatrix(float *l, float *e, float *n) {
   
   // These are c and d (corresponding to the tutorial)
   float d = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
   float c = e[0]*n[0] + e[1]*n[1] + e[2]*n[2] - d;
   
   // Create the matrix. OpenGL uses column by column ordering
   shadowMatrix[0]  = l[0]*n[0]+c;
   shadowMatrix[4]  = n[1]*l[0];
   shadowMatrix[8]  = n[2]*l[0];
   shadowMatrix[12] = -l[0]*c-l[0]*d;
  
   shadowMatrix[1]  = n[0]*l[1];
   shadowMatrix[5]  = l[1]*n[1]+c;
   shadowMatrix[9]  = n[2]*l[1];
   shadowMatrix[13] = -l[1]*c-l[1]*d;
  
   shadowMatrix[2]  = n[0]*l[2];
   shadowMatrix[6]  = n[1]*l[2];
   shadowMatrix[10] = l[2]*n[2]+c;
   shadowMatrix[14] = -l[2]*c-l[2]*d;
  
   shadowMatrix[3]  = n[0];
   shadowMatrix[7]  = n[1];
   shadowMatrix[11] = n[2];
   shadowMatrix[15] = -d;
}

void OpenGLFrame::genWorldLimits() {
   
   if (model != 0) {
      mincoo[0] = model->mincoo[0]; mincoo[1] = model->mincoo[1]; mincoo[2] = model->mincoo[2];
      maxcoo[0] = model->maxcoo[0]; maxcoo[1] = model->maxcoo[1]; maxcoo[2] = model->maxcoo[2];
   }
   else {
      mincoo[0] = -1; mincoo[1] = -1; mincoo[2] = -1;
      maxcoo[0] =  1; maxcoo[1] =  1; maxcoo[2] =  1;
   }
   
   float xc = mincoo[0] + 0.5*(maxcoo[0]-mincoo[0]);
   float yc = mincoo[1] + 0.5*(maxcoo[1]-mincoo[1]);
   
   float WorldWidth  = abs(maxcoo[0]-mincoo[0]);
   float WorldHeight = abs(maxcoo[1]-mincoo[1]);
   
   if ((WorldHeight/WorldWidth) > ((float)height()/(float)width())) {
      WorldWidth = WorldHeight / ((float)height()/(float)width());
   }
   else if ((WorldHeight/WorldWidth) < ((float)height()/(float)width())) {
      WorldHeight = WorldWidth * ((float)height()/(float)width());
   }
   
   // world limits
   mincoo[0] = xc - 0.5*WorldWidth;
   mincoo[1] = yc - 0.5*WorldHeight;
   
   maxcoo[0] = xc + 0.5*WorldWidth;
   maxcoo[1] = yc + 0.5*WorldHeight;
   
   // center camera
   camcenter[0] = xc;
   camcenter[1] = yc;
   camcenter[2] = mincoo[2] + 0.5*(maxcoo[2]-mincoo[2]);
}

/// It sets the projection/modelview matrices
void OpenGLFrame::setGLview() {
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   
   WorldWidth  = abs(maxcoo[0]-mincoo[0])*0.5;
   WorldHeight = abs(maxcoo[1]-mincoo[1])*0.5;
   
   objDistance = 0.8660*(maxcoo[1]-mincoo[1]); /// sqrt(3)/2 = 0.8660
   //~ objDistance = WorldHeight;
   nearplane = objDistance - objDistance*0.5;
   farplane  = nearplane + 20.0;
   
   cameye[0] = camcenter[0];
   cameye[1] = camcenter[1];
   cameye[2] = maxcoo[2] + objDistance + 5;
   
   if (isProjOrtho) 
      glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
              -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
               nearplane/2, 3*farplane);
   else 
      gluPerspective(fovy*zoomFactor, (GLfloat)width()/(GLfloat)height(), nearplane, farplane);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glViewport(0, 0, width(), height());
}
   
/// A simple resize callback.
/// @param width the width of the new window.
/// @param height the height of the new window.
void OpenGLFrame::resizeGL(int width, int height) {
   
   setGLview();
   arcball.setBounds(width, height);  //Update mouse bounds for the arcball
}

/// Callback for handling mouse button press.
/// @param event encapsulates the event's info.
void OpenGLFrame::mousePressEvent(QMouseEvent *event) {
   
   xini=xlast=event->x(); yini=ylast=event->y();
   if (event->button() == Qt::LeftButton) {
      cout<<"Qt::LeftButton press... "<<endl;
      list<uint> selected;
      if (interactionMode == PAINTING_MODE) {
         // transforming the eye vector = (0,0,-1) ... 
         Number x = -sceneT[2], y = -sceneT[6], z = -sceneT[10];
         Vector3 eye(x,y,z);
         selection.begin(event->x(),height()-event->y());
         glDrawModelFacesFront(model, eye);
         selection.end(selected);
         if (selected.size() > 0) {
            Facet_iterator fi = model->facets_begin();
            advance(fi, selected.front());
            idSelected = fi->halfedge()->vertex()->rigging_group;
            idLastSelected = idSelected;
         }
      }
      else if (interactionMode == SELECTION_MODE) {
         selection.begin(event->x(),height()-event->y());
         glDrawSkeletonJoints(skeleton);
         selection.end(selected);
         if (selected.size() > 0) {
            idLastJoint = selected.front();
            cout<<"clicked joint id: "<<idLastJoint<<endl;
         }
         else {
            selection.begin(event->x(),height()-event->y());
            glDrawModelFaces(model);
            selection.end(selected);
            if (selected.size() > 1) {
               Point3 c = model->getMidPointBetweenTwoFaces(selected.front(), selected.back());
               int idJoint = skeleton->addJoint(c);
               skeleton->addBone(idLastJoint, idJoint);
               idLastJoint = idJoint;
            }
            else
               idLastJoint = -1;
         }
      }
   }
   else if (event->button() == Qt::MidButton) {
      arcball.click(event->x(),height()-event->y());
   }
   else if (event->button() == Qt::RightButton) {
      grabLast = grabStart = screenToWorld(event->x(), height()-event->y());
   }
   sceneIniT = sceneT;
   glDraw();
}

/// Callback for handling mouse movements.
/// @param event encapsulates the event's info.
void OpenGLFrame::mouseMoveEvent(QMouseEvent *event) {
   
   if (event->buttons() & Qt::LeftButton) {
      if (interactionMode == SELECTION_MODE and idLastJoint>=0) {
         cout<<"Qt::LeftButton move ... SELECTION_MODE "<<idLastJoint<<endl;
         int x=event->x(), y=height()-event->y();
         Vector3 t = getTranslationWorld(x,y,xlast,height()-ylast);
         skeleton->translateJoint(idLastJoint, t);
         xlast=event->x();
         ylast=event->y();
      }
      else if (interactionMode == PAINTING_MODE and idSelected>=0) {
         cout<<"Qt::LeftButton move ... PAINTING_MODE "<<idSelected<<endl;
         // transforming the eye vector = (0,0,-1) ... 
         Number x = -sceneT[2], y = -sceneT[6], z = -sceneT[10]; // 
         Vector3 eye(x,y,z);
         list<uint> selected;
         selection.begin(event->x(),height()-event->y(), paintCursorW, paintCursorH);
         glDrawModelVerticesFront(model, eye);
         selection.endAll(selected);
         cout<<" ...selected.size(): "<<selected.size()<<endl;
         if (selected.size() >= 1) {
            list<uint>::iterator it = selected.begin();
            while(it != selected.end()) {
               Vertex_iterator vi = model->vertices_begin();
               advance(vi, *it);
               vi->rigging_group = idSelected;
               ++it;
            }
         }
      }
   }
   else if ((event->buttons() & Qt::MidButton)) { // rotations handler
      AMatrix<float> mT;
      arcball.drag(event->x(), height()-event->y(), &mT);
      sceneT = mT*sceneIniT;
   }
   else if (event->buttons() & Qt::RightButton) { // translations handler
      Vector3 t = screenToWorld(xini, yini, event->x(), height()-event->y());
      AMatrix<float> mT;
      mT.identity();
      mT.translation(t[0], t[1], t[2]);
      sceneT = sceneIniT*mT;
   }
   event->accept();
   glDraw();
}

/// Callback for handling mouse button release.
/// @param event encapsulates the event's info.
void OpenGLFrame::mouseReleaseEvent(QMouseEvent *event) {
   
   if (event->button() == Qt::LeftButton) {
      if (interactionMode == SELECTION_MODE and (xini!=event->x() or yini!=event->y())) { // there was a dragging
         idLastJoint = -1;
      }
      else if (interactionMode == PAINTING_MODE and idSelected<0 and idLastSelected>=0) {
         cout<<"Qt::LeftButton move ... PAINTING_MODE "<<idSelected<<endl;
         // transforming the eye vector = (0,0,-1) ... 
         Number x = -sceneT[2], y = -sceneT[6], z = -sceneT[10]; // 
         Vector3 eye(x,y,z);
         list<uint> selected;
         selection.begin(xini, yini, event->x(), height()-event->y());
         glDrawModelVerticesFront(model, eye);
         selection.endAll(selected);
         cout<<" ...selected.size(): "<<selected.size()<<endl;
         if (selected.size() >= 1) {
            list<uint>::iterator it = selected.begin();
            while(it != selected.end()) {
               Vertex_iterator vi = model->vertices_begin();
               advance(vi, *it);
               vi->rigging_group = idLastSelected;
               ++it;
            }
         }
      }
   }
   idSelected = -1;
   event->accept();
   glDraw();
}

/// Inplements zoom operations using the mouse wheel
void OpenGLFrame::wheelEvent(QWheelEvent *e) {
   
   //~ if (e->delta()>0) zoomFactor+= 0.1;
   //~ else              zoomFactor-= 0.1;
   //~ e->accept(); 
   //~ setGLview();
   //~ glDraw();
   
   if (e->delta()>0) zoomIn();
   else              zoomOut();
      
   e->accept();
}

void OpenGLFrame::keyPressEvent(QKeyEvent *event) {
   
   switch(event->key()) {
      case Qt::Key_Escape: exit(0);
      case Qt::Key_J: clusteringOnBones = false; break;
      case Qt::Key_B: clusteringOnBones = true; break;
      case Qt::Key_S: viewSkeleton = !viewSkeleton; break;
      case Qt::Key_R: {
         if (clusteringOnBones) {
            ClusteringOnBones clusterer(model, skeleton);
            clusterer.execute();
         }
         else {
            ClusteringOnJoints clusterer(model, skeleton);
            clusterer.execute();
         }
         break;
      }
      case Qt::Key_K: {
         if (clusteringOnBones) {
            Skinning skinner(model, skeleton->nBones());
            skinner.execute();
         }
         else {
            Skinning skinner(model, skeleton->nJoints());
            skinner.execute();
         }
         break;
      }
      case Qt::Key_N: { 
         idVertex+=1; 
         cout<<"idvertex: "<<idVertex<<endl;
         break;
      }
      case Qt::Key_E: { 
         cout<<"exporting ..."<<endl;
         model->save_skinningOnJoints_assoc("export.assoc");
         cout<<"exporting ... done!"<<endl;
         break;
      }
      case Qt::Key_O: {
         AMatrix<float> mT;
         mT.identity();
         mT.rotation(5.0*(3.1416/180.0), 0, -1, 0);
         sceneT = sceneT*mT;
         break;
      }
      case Qt::Key_Plus: {
         paintCursorW++;
         paintCursorH++;
         cout<<":> "<<paintCursorW<<" : "<<paintCursorH<<endl;
         break;
      }
      case Qt::Key_Minus: {
         if (paintCursorW>1) --paintCursorW;
         if (paintCursorH>1) --paintCursorH;
         cout<<":> "<<paintCursorW<<" : "<<paintCursorH<<endl;
         break;
      }
   }
   event->accept();
   glDraw();
}

/// Implements the "zoomin" operation
void OpenGLFrame::zoomIn () {
   
   AMatrix<float> mT;
   mT.identity();
   mT.scaling(1.2);
   sceneT = mT*sceneT;
   
   glDraw();
}

/// Implements the "zoomout" operation
void OpenGLFrame::zoomOut () {
   
   AMatrix<float> mT;
   mT.identity();
   mT.scaling(1.0/1.2);
   sceneT = mT*sceneT;
   
   glDraw();
}

/// Implements the "zoomall" operation
void OpenGLFrame::zoomAll () {
   
   sceneT.identity();
   glDraw();
}

/// Returns the world coordinates of a point in screen space
Point3 OpenGLFrame::screenToWorld(int x, int y) {
   
   glGetIntegerv(GL_VIEWPORT, viewport);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   
   GLdouble xw, yw, zw;
   gluUnProject(x, y, 1.0, mMtx, pMtx, viewport, &xw, &yw, &zw);
   return Point3(xw, yw, zw);
}

/// Returns the translation vector in world coordinates of two points in screen space
Vector3 OpenGLFrame::screenToWorld(int xi, int yi, int xe, int ye) {
   
   glGetIntegerv(GL_VIEWPORT, viewport);
   //~ glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   
   /// Compute the world coordinates using the orthographic projection
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
           -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
            nearplane, farplane);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glPopMatrix();
   
   GLdouble ws[3], we[3]; // project on the near plane
   gluUnProject(xi, yi, 0, mMtx, pMtx, viewport, &ws[0], &ws[1], &ws[2]);
   gluUnProject(xe, ye, 0, mMtx, pMtx, viewport, &we[0], &we[1], &we[2]);
   
   return Vector3(we[0]-ws[0], we[1]-ws[1], we[2]-ws[2]);
}

Vector3 OpenGLFrame::getTranslationWorld(int x, int y, int xlastt, int ylastt) {
   
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetIntegerv(GL_VIEWPORT, viewport);
   
   GLdouble m[]={1,0,0,0,
              0,1,0,0,
              0,0,1,0,
              0,0,0,1};
   
   double wbx, wby, wbz, wex, wey, wez;
   gluUnProject(xlastt, ylastt, 0.0, mMtx, pMtx, viewport, &wbx, &wby, &wbz);
   gluUnProject(x, y, 0.0, mMtx, pMtx, viewport, &wex, &wey, &wez);
   return Vector3(wex-wbx, wey-wby, wez-wbz);
}

void OpenGLFrame::loadClosestBone(const QString &filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "cb");
   model->load_closestBone(filename.toStdString().c_str());
   
   cout<<"computing the dClosestJoint ... "<<endl;
   // compute the distance to the bone
   Number t=0;
   Vertex_iterator vi = model->vertices_begin();
   while (vi != model->vertices_end()) {
      int idbone = vi->rigging_group;
      const Point3 &p = vi->point();
      vi->dClosestJoint = skeleton->getDistance(p, idbone, t);
      ++vi;
   }
   cout<<"computing the dClosestJoint ... done! "<<endl;
}

void OpenGLFrame::saveClosestBone(const QString &filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "cb");
   model->save_closestBone(filename.toStdString().c_str());
}

void OpenGLFrame::loadClosestJoint(const QString &filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "cj");
   model->load_closestJoint(filename.toStdString().c_str());
   
   cout<<"computing the dClosestJoint ... "<<endl;
   // compute the distance to the joint
   Vertex_iterator vi = model->vertices_begin();
   while (vi != model->vertices_end()) {
      int idjoint = vi->rigging_group;
      const Point3 &p = vi->point();
      const Point3 &j = skeleton->getJointPos(idjoint);
      const Vector3 v(p,j);
      vi->dClosestJoint = sqrt(v*v);
      ++vi;
   }
   cout<<"computing the dClosestJoint ... done! "<<endl;
}

void OpenGLFrame::saveClosestJoint(const QString &filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "cj");
   model->save_closestJoint(filename.toStdString().c_str());
}

void OpenGLFrame::loadSkinningOnJoints(const QString &filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "soj");
   model->load_skinningOnJoints(filename.toStdString().c_str());
}

void OpenGLFrame::saveSkinningOnJoints(const QString &filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "soj");
   model->save_skinningOnJoints(filename.toStdString().c_str());
}

void OpenGLFrame::loadSkinningOnBones(const QString& filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "sob");
   model->load_skinningOnBones(filename.toStdString().c_str());
}

void OpenGLFrame::saveSkinningOnBones(const QString& filename) {
   
   assert(model != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "sob");
   model->save_skinningOnBones(filename.toStdString().c_str());
}
